package programme;
import java.util.ArrayList;

/**
 * Classe implémentant une expression.<br/>
 * Une expression est une somme de sous-expressions et utlisée dans la fonction objectif et les contraintes.<br />
 * <em>Exemple : <em>x1 + 3*x2 + 10*x3
 * 
 *
 */
public class Expression {

	/** Programme auquel appartient l'expression */
	Programme prog;
	
	/** Liste des sous-expressions*/
	private ArrayList<SousExpression> ssExpressions;
	
	public Expression(Programme p){
		this.prog = p;
		ssExpressions = new ArrayList<SousExpression>();
	}

	public Expression(){
		ssExpressions = new ArrayList<SousExpression>();
	}
	
	public ArrayList<SousExpression> getSsExpressions() {
		return ssExpressions;
	}

	public void setSsExpressions(ArrayList<SousExpression> ssExpressions) {
		this.ssExpressions = ssExpressions;
	}

	public void addSousExpression(SousExpression sousExpression){
		ssExpressions.add(sousExpression);
	}

	/**
	 * Ajoute la sous-expression coef*v1*v2 à l'expression
	 * @param coef
	 * @param v1
	 * @param v2
	 */
	public void addProduit(double coef, Variable v1, Variable v2){
		SousExpression s = new SousExpression();
		s.setCoef(coef);
		ArrayList<Variable> se = new ArrayList<Variable>();
		se.add(v1);
		se.add(v2);
		s.setVar(se);
		ssExpressions.add(s);
	}
	
	/**
	 * Ajoute la sous-expression coef*var à l'expression
	 * @param coef
	 * @param var
	 */
	public void addSomme(double coef, Variable var){
		SousExpression s = new SousExpression();
		s.setCoef(coef);
		s.addVariable(var);
		ssExpressions.add(s);
	}
	
	/**
	 * Ajoute la sous-expression 1*var à l'expression
	 * @param var
	 */
	public void addVar(Variable var){
		SousExpression s = new SousExpression();
		s.setCoef(1);
		s.addVariable(var);
		ssExpressions.add(s);
	}	
	
	public void multiplyByVar(VecteurVariables v, int i){
		for(SousExpression e:ssExpressions){
			e.addVariable(new Variable(v,i, TypeVariable.CONTINUE, 0, Double.MAX_VALUE));
			i ++;
		}
	}
	
	public void lineariser(){
		// On ajoute le vecteur de variable pour les variables qu on va ajouter 
		// pour la linearisation
		prog.addVecteurVariables(new VecteurVariables("Q"));
		VecteurVariables Q = prog.getVecteursVariables().get("Q");
		
		// On cherche dans les sous expressions
	
		// Si la sous expressions est quadratique
		for(SousExpression exp:ssExpressions){
			ArrayList<Variable> vars = exp.getVar();
			if( vars.size() > 1){
				int i = vars.get(0).getNum();
				int j = vars.get(1).getNum();
				
				// Addition de Qij dans le vecteur variable Q
				// Note : on ajoute beaucoup de variable en trop car
				// Qij = Qji alors que l'on crée 2 variables
				// Pour notre défense c'est indiquer tel quel dans le sujet... 
				// Si l'on voulait optimiser le nombre de variables (et donc de requetes)
				// il faudrais changer ca. 
				Variable var = new Variable(Q, i+j, TypeVariable.CONTINUE, 0, 1);
//				Q.addVariable(var);
				
				// On remplace le terme quadratique par la nouvelle variable
				exp.setVar(var);
				
				// On ajoute les nouvelles contraintes en rapport à Qij
				// A savoir:
				// Qij <= xi
				// Qij <= xj
				// Qij >= xi + xj - 1
				Expression e1 = new Expression(prog);
				e1.addVar(var);
				e1.addSomme(-1, vars.get(0));
				prog.addContrainte(new Contrainte(e1, TypeContrainte.INF, 0, false));
				
				Expression e2 = new Expression(prog);
				e2.addVar(var);
				e2.addSomme(-1, vars.get(1));
				prog.addContrainte(new Contrainte(e2, TypeContrainte.INF, 0, false));
				
				Expression e3 = new Expression(prog);
				e3.addSomme(-1, var);
				e3.addVar(vars.get(0));
				e3.addVar(vars.get(1));
				prog.addContrainte(new Contrainte(e3, TypeContrainte.INF, 1, false));
				
			}	
		}
	}
	
	public String toString(){
		StringBuffer buf = new StringBuffer();
		for (SousExpression se : ssExpressions){
			buf.append(se.coef + " . ");
			for (Variable v : se.getVar()){
				// element *= s.getValeurs().get(v);
				buf.append( v + " . ");
			}
			buf.append(" + ");
		}
		return buf.toString();
	}
}
